package cn.kgc.filter;

import cn.hutool.core.collection.CollectionUtil;
import cn.kgc.base.dto.AdminUserDTO;
import cn.kgc.base.entity.JwtToken;
import cn.kgc.base.entity.LoginUserInfo;
import cn.kgc.commons.constant.SystemConstant;
import cn.kgc.commons.util.RedisUtil;
import cn.kgc.commons.util.UserContextHolder;
import cn.kgc.constant.GateWayConstant;
import cn.kgc.util.JwtTokenParseUtil;
import com.alibaba.fastjson.JSON;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;
import java.nio.CharBuffer;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;

/**
 * @author 王跃
 */
@Component
public class GlobalLoginRegisterFilter implements GlobalFilter, Ordered {
    @Resource
    private DiscoveryClient discoveryClient;

    @Resource
    private RestTemplate restTemplate;

    @Resource
    private RedisUtil redisUtil;

    //获得请求中的JSON格式的参数
    private String parseRequest(ServerHttpRequest request){
        //获得请求体(包含参数)
    Flux<DataBuffer> dataBufferFlux= request.getBody();
    //拼接获得请求中的参数的值
    AtomicReference<CharBuffer> atomicReference=new AtomicReference<>();
    //从请求中获得参数
        dataBufferFlux.subscribe(dataBuffer -> {
        DataBufferUtils.release(dataBuffer);
        //将dataBuffer中的数据转成CharBuffer
        CharBuffer charBuffer= StandardCharsets.UTF_8.decode(dataBuffer.asByteBuffer());
        atomicReference.set(charBuffer);
    });
    return atomicReference.toString();  //请求体中的参数
}

    //认证登录获得令牌信息
    private String generateToken(ServerHttpRequest request,String urlFormat){
        List<ServiceInstance> list=
                  discoveryClient.getInstances("mall-authority-center");
        if(CollectionUtil.isEmpty(list)){ //获得认证服务为空,令牌一定为空
            return null;
        }
        ServiceInstance serviceInstance= list.get(0);
        String host= serviceInstance.getHost();
        int port= serviceInstance.getPort();
        String url= String.format(urlFormat,host,port);

        //获得请求中的json格式的参数,转成auth认证的时候需要传递的参数类型
        String requestParams= parseRequest(request);

        AdminUserDTO adminUserDTO=
                JSON.parseObject(requestParams, AdminUserDTO.class);

        HttpHeaders headers=new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
        HttpEntity httpEntity=new HttpEntity(adminUserDTO,headers); //请求实体
        //发送请求
        JwtToken jwtToken=
                restTemplate.postForObject(url,httpEntity,JwtToken.class);
        if(jwtToken!=null){
            return  jwtToken.getToken();
        }
        return null;
    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest serverHttpRequest= exchange.getRequest();
        ServerHttpResponse serverHttpResponse= exchange.getResponse();
        String uri= serverHttpRequest.getURI().toString();

        if(uri.contains(GateWayConstant.LOGIN_URL)) {  //登录操作,调用认证服务,根据账号密码获得令牌
            //后端auth认证时登录的地址
            String loginUri=GateWayConstant.URL_FORMAT_LOGIN;
            String token=generateToken(serverHttpRequest,loginUri);
            if(token==null){
                serverHttpResponse.setStatusCode(HttpStatus.UNAUTHORIZED);
            }else{
                serverHttpResponse.setStatusCode(HttpStatus.OK);
            }
            serverHttpResponse.getHeaders().add(
                    SystemConstant.JWT_USER_INFO_KEY,token);
            return serverHttpResponse.setComplete();
        }

        if(uri.contains(GateWayConstant.REGISTER_URL)) { //注册操作
            //后端auth注册的地址
            String registerUrl=GateWayConstant.URL_FORMAT_REGISTER;
            String token=generateToken(serverHttpRequest,registerUrl);
            if(token==null){
                serverHttpResponse.setStatusCode(HttpStatus.UNAUTHORIZED);
            }else{
                serverHttpResponse.setStatusCode(HttpStatus.OK);
            }
            serverHttpResponse.getHeaders().add(
                    SystemConstant.JWT_USER_INFO_KEY,token);
            return serverHttpResponse.setComplete();
        }

        //如果是发送验证码,直接放行
        if (uri.contains(GateWayConstant.URL_FORMAT_AUTH_CODE)){
            return chain.filter(exchange);
        }
        //从请求头中获得令牌
        String token= serverHttpRequest.getHeaders()
                          .getFirst(SystemConstant.JWT_USER_INFO_KEY);
        if(token==null){
            serverHttpResponse.setStatusCode(HttpStatus.UNAUTHORIZED);
            return serverHttpResponse.setComplete();
        }
        LoginUserInfo loginUserInfo=null;
        try {
             loginUserInfo=(LoginUserInfo) JwtTokenParseUtil.parseToken(token);
        } catch (Exception e) {  //解析令牌错误
            serverHttpResponse.setStatusCode(HttpStatus.UNAUTHORIZED);
            return serverHttpResponse.setComplete();
        }
        //令牌解析成功
        String id= loginUserInfo.getId()+"";
        String name= loginUserInfo.getUsername();
        //拿到ServerHttpRequest.Builder 可以给request添加信息
        ServerHttpRequest.Builder builder= serverHttpRequest.mutate();
        builder.header(UserContextHolder.LOGIN_USER_ID,id);
        builder.header(UserContextHolder.LOGIN_USER_NAME,name);
        return chain.filter(exchange.mutate().request(builder.build()).build());
    }

    @Override
    public int getOrder() {
        return Ordered.HIGHEST_PRECEDENCE+1;
    }
}
